//#import "HTTPServer.h"
//#import "GCDAsyncSocket.h"
//#import "HTTPConnection.h"
//#import "WebSocket.h"
//#import "HTTPLogging.h"
//
//#if ! __has_feature(objc_arc)
//#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
//#endif
//
//// Log levels: off, error, warn, info, verbose
//// Other flags: trace
//static const int httpLogLevel = HTTP_LOG_LEVEL_INFO; // | HTTP_LOG_FLAG_TRACE;
//
//@interface HTTPServer (PrivateAPI)
//
//- (void)unpublishBonjour;
//- (void)publishBonjour;
//
//+ (void)startBonjourThreadIfNeeded;
//+ (void)performBonjourBlock:(dispatch_block_t)block;
//
//@end
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark -
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//@implementation HTTPServer
//
///**
// * Standard Constructor.
// * Instantiates an HTTP server, but does not start it.
//**/
//- (id)init
//{
//	if ((self = [super init]))
//	{
//		HTTPLogTrace();
//		
//		// Setup underlying dispatch queues
//		serverQueue = dispatch_queue_create("HTTPServer", NULL);
//		connectionQueue = dispatch_queue_create("HTTPConnection", NULL);
//		
//		IsOnServerQueueKey = &IsOnServerQueueKey;
//		IsOnConnectionQueueKey = &IsOnConnectionQueueKey;
//		
//		void *nonNullUnusedPointer = (__bridge void *)self; // Whatever, just not null
//		
//		dispatch_queue_set_specific(serverQueue, IsOnServerQueueKey, nonNullUnusedPointer, NULL);
//		dispatch_queue_set_specific(connectionQueue, IsOnConnectionQueueKey, nonNullUnusedPointer, NULL);
//		
//		// Initialize underlying GCD based tcp socket
//		asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:serverQueue];
//		
//		// Use default connection class of HTTPConnection
//		connectionClass = [HTTPConnection self];
//		
//		// By default bind on all available interfaces, en1, wifi etc
//		interface = nil;
//		
//		// Use a default port of 0
//		// This will allow the kernel to automatically pick an open port for us
//		port = 0;
//		
//		// Configure default values for bonjour service
//		
//		// Bonjour domain. Use the local domain by default
//		domain = @"local.";
//		
//		// If using an empty string ("") for the service name when registering,
//		// the system will automatically use the "Computer Name".
//		// Passing in an empty string will also handle name conflicts
//		// by automatically appending a digit to the end of the name.
//		name = @"";
//		
//		// Initialize arrays to hold all the HTTP and webSocket connections
//		connections = [[NSMutableArray alloc] init];
//		webSockets  = [[NSMutableArray alloc] init];
//		
//		connectionsLock = [[NSLock alloc] init];
//		webSocketsLock  = [[NSLock alloc] init];
//		
//		// Register for notifications of closed connections
//		[[NSNotificationCenter defaultCenter] addObserver:self
//		                                         selector:@selector(connectionDidDie:)
//		                                             name:HTTPConnectionDidDieNotification
//		                                           object:nil];
//		
//		// Register for notifications of closed websocket connections
//		[[NSNotificationCenter defaultCenter] addObserver:self
//		                                         selector:@selector(webSocketDidDie:)
//		                                             name:WebSocketDidDieNotification
//		                                           object:nil];
//		
//		isRunning = NO;
//	}
//	return self;
//}
//
///**
// * Standard Deconstructor.
// * Stops the server, and clients, and releases any resources connected with this instance.
//**/
//- (void)dealloc
//{
//	HTTPLogTrace();
//	
//	// Remove notification observer
//	[[NSNotificationCenter defaultCenter] removeObserver:self];
//	
//	// Stop the server if it's running
//	[self stop];
//	
//	// Release all instance variables
//	
//	#if !OS_OBJECT_USE_OBJC
//	dispatch_release(serverQueue);
//	dispatch_release(connectionQueue);
//	#endif
//	
//	[asyncSocket setDelegate:nil delegateQueue:NULL];
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark Server Configuration
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
///**
// * The document root is filesystem root for the webserver.
// * Thus requests for /index.html will be referencing the index.html file within the document root directory.
// * All file requests are relative to this document root.
//**/
//- (NSString *)documentRoot
//{
//	__block NSString *result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = documentRoot;
//	});
//	
//	return result;
//}
//
//- (void)setDocumentRoot:(NSString *)value
//{
//	HTTPLogTrace();
//	
//	// Document root used to be of type NSURL.
//	// Add type checking for early warning to developers upgrading from older versions.
//	
//	if (value && ![value isKindOfClass:[NSString class]])
//	{
//		HTTPLogWarn(@"%@: %@ - Expecting NSString parameter, received %@ parameter",
//					THIS_FILE, THIS_METHOD, NSStringFromClass([value class]));
//		return;
//	}
//	
//	NSString *valueCopy = [value copy];
//	
//	dispatch_async(serverQueue, ^{
//		documentRoot = valueCopy;
//	});
//	
//}
//
///**
// * The connection class is the class that will be used to handle connections.
// * That is, when a new connection is created, an instance of this class will be intialized.
// * The default connection class is HTTPConnection.
// * If you use a different connection class, it is assumed that the class extends HTTPConnection
//**/
//- (Class)connectionClass
//{
//	__block Class result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = connectionClass;
//	});
//	
//	return result;
//}
//
//- (void)setConnectionClass:(Class)value
//{
//	HTTPLogTrace();
//	
//	dispatch_async(serverQueue, ^{
//		connectionClass = value;
//	});
//}
//
///**
// * What interface to bind the listening socket to.
//**/
//- (NSString *)interface
//{
//	__block NSString *result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = interface;
//	});
//	
//	return result;
//}
//
//- (void)setInterface:(NSString *)value
//{
//	NSString *valueCopy = [value copy];
//	
//	dispatch_async(serverQueue, ^{
//		interface = valueCopy;
//	});
//	
//}
//
///**
// * The port to listen for connections on.
// * By default this port is initially set to zero, which allows the kernel to pick an available port for us.
// * After the HTTP server has started, the port being used may be obtained by this method.
//**/
//- (UInt16)port
//{
//	__block UInt16 result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = port;
//	});
//	
//    return result;
//}
//
//- (UInt16)listeningPort
//{
//	__block UInt16 result;
//	
//	dispatch_sync(serverQueue, ^{
//		if (isRunning)
//			result = [asyncSocket localPort];
//		else
//			result = 0;
//	});
//	
//	return result;
//}
//
//- (void)setPort:(UInt16)value
//{
//	HTTPLogTrace();
//	
//	dispatch_async(serverQueue, ^{
//		port = value;
//	});
//}
//
///**
// * Domain on which to broadcast this service via Bonjour.
// * The default domain is @"local".
//**/
//- (NSString *)domain
//{
//	__block NSString *result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = domain;
//	});
//	
//    return result;
//}
//
//- (void)setDomain:(NSString *)value
//{
//	HTTPLogTrace();
//	
//	NSString *valueCopy = [value copy];
//	
//	dispatch_async(serverQueue, ^{
//		domain = valueCopy;
//	});
//	
//}
//
///**
// * The name to use for this service via Bonjour.
// * The default name is an empty string,
// * which should result in the published name being the host name of the computer.
//**/
//- (NSString *)name
//{
//	__block NSString *result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = name;
//	});
//	
//	return result;
//}
//
//- (NSString *)publishedName
//{
//	__block NSString *result;
//	
//	dispatch_sync(serverQueue, ^{
//		
//		if (netService == nil)
//		{
//			result = nil;
//		}
//		else
//		{
//			
//			dispatch_block_t bonjourBlock = ^{
//				result = [[netService name] copy];
//			};
//			
//			[[self class] performBonjourBlock:bonjourBlock];
//		}
//	});
//	
//	return result;
//}
//
//- (void)setName:(NSString *)value
//{
//	NSString *valueCopy = [value copy];
//	
//	dispatch_async(serverQueue, ^{
//		name = valueCopy;
//	});
//	
//}
//
///**
// * The type of service to publish via Bonjour.
// * No type is set by default, and one must be set in order for the service to be published.
//**/
//- (NSString *)type
//{
//	__block NSString *result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = type;
//	});
//	
//	return result;
//}
//
//- (void)setType:(NSString *)value
//{
//	NSString *valueCopy = [value copy];
//	
//	dispatch_async(serverQueue, ^{
//		type = valueCopy;
//	});
//	
//}
//
///**
// * The extra data to use for this service via Bonjour.
//**/
//- (NSDictionary *)TXTRecordDictionary
//{
//	__block NSDictionary *result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = txtRecordDictionary;
//	});
//	
//	return result;
//}
//
//- (void)setTXTRecordDictionary:(NSDictionary *)value
//{
//	HTTPLogTrace();
//	
//	NSDictionary *valueCopy = [value copy];
//	
//	dispatch_async(serverQueue, ^{
//	
//		txtRecordDictionary = valueCopy;
//		
//		// Update the txtRecord of the netService if it has already been published
//		if (netService)
//		{
//			NSNetService *theNetService = netService;
//			NSData *txtRecordData = nil;
//			if (txtRecordDictionary)
//				txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary];
//			
//			dispatch_block_t bonjourBlock = ^{
//				[theNetService setTXTRecordData:txtRecordData];
//			};
//			
//			[[self class] performBonjourBlock:bonjourBlock];
//		}
//	});
//	
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark Server Control
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//- (BOOL)start:(NSError **)errPtr
//{
//	HTTPLogTrace();
//	
//	__block BOOL success = YES;
//	__block NSError *err = nil;
//	
//	dispatch_sync(serverQueue, ^{ @autoreleasepool {
//		
//		success = [asyncSocket acceptOnInterface:interface port:port error:&err];
//		if (success)
//		{
//			HTTPLogInfo(@"%@: Started HTTP server on port %hu", THIS_FILE, [asyncSocket localPort]);
//			
//			isRunning = YES;
//			[self publishBonjour];
//		}
//		else
//		{
//			HTTPLogError(@"%@: Failed to start HTTP Server: %@", THIS_FILE, err);
//		}
//	}});
//	
//	if (errPtr)
//		*errPtr = err;
//	
//	return success;
//}
//
//- (void)stop
//{
//	[self stop:NO];
//}
//
//- (void)stop:(BOOL)keepExistingConnections
//{
//	HTTPLogTrace();
//	
//	dispatch_sync(serverQueue, ^{ @autoreleasepool {
//		
//		// First stop publishing the service via bonjour
//		[self unpublishBonjour];
//		
//		// Stop listening / accepting incoming connections
//		[asyncSocket disconnect];
//		isRunning = NO;
//		
//		if (!keepExistingConnections)
//		{
//			// Stop all HTTP connections the server owns
//			[connectionsLock lock];
//			for (HTTPConnection *connection in connections)
//			{
//				[connection stop];
//			}
//			[connections removeAllObjects];
//			[connectionsLock unlock];
//			
//			// Stop all WebSocket connections the server owns
//			[webSocketsLock lock];
//			for (WebSocket *webSocket in webSockets)
//			{
//				[webSocket stop];
//			}
//			[webSockets removeAllObjects];
//			[webSocketsLock unlock];
//		}
//	}});
//}
//
//- (BOOL)isRunning
//{
//	__block BOOL result;
//	
//	dispatch_sync(serverQueue, ^{
//		result = isRunning;
//	});
//	
//	return result;
//}
//
//- (void)addWebSocket:(WebSocket *)ws
//{
//	[webSocketsLock lock];
//	
//	HTTPLogTrace();
//	[webSockets addObject:ws];
//	
//	[webSocketsLock unlock];
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark Server Status
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
///**
// * Returns the number of http client connections that are currently connected to the server.
//**/
//- (NSUInteger)numberOfHTTPConnections
//{
//	NSUInteger result = 0;
//	
//	[connectionsLock lock];
//	result = [connections count];
//	[connectionsLock unlock];
//	
//	return result;
//}
//
///**
// * Returns the number of websocket client connections that are currently connected to the server.
//**/
//- (NSUInteger)numberOfWebSocketConnections
//{
//	NSUInteger result = 0;
//	
//	[webSocketsLock lock];
//	result = [webSockets count];
//	[webSocketsLock unlock];
//	
//	return result;
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark Incoming Connections
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//- (HTTPConfig *)config
//{
//	// Override me if you want to provide a custom config to the new connection.
//	// 
//	// Generally this involves overriding the HTTPConfig class to include any custom settings,
//	// and then having this method return an instance of 'MyHTTPConfig'.
//	
//	// Note: Think you can make the server faster by putting each connection on its own queue?
//	// Then benchmark it before and after and discover for yourself the shocking truth!
//	// 
//	// Try the apache benchmark tool (already installed on your Mac):
//	// $  ab -n 1000 -c 1 http://localhost:<port>/some_path.html
//	
//	return [[HTTPConfig alloc] initWithServer:self documentRoot:documentRoot queue:connectionQueue];
//}
//
//- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
//{
//	HTTPConnection *newConnection = (HTTPConnection *)[[connectionClass alloc] initWithAsyncSocket:newSocket
//	                                                                                 configuration:[self config]];
//	[connectionsLock lock];
//	[connections addObject:newConnection];
//	[connectionsLock unlock];
//	
//	[newConnection start];
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark Bonjour
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//- (void)publishBonjour
//{
//	HTTPLogTrace();
//	
//	NSAssert(dispatch_get_specific(IsOnServerQueueKey) != NULL, @"Must be on serverQueue");
//	
//	if (type)
//	{
//		netService = [[NSNetService alloc] initWithDomain:domain type:type name:name port:[asyncSocket localPort]];
//		[netService setDelegate:self];
//		
//		NSNetService *theNetService = netService;
//		NSData *txtRecordData = nil;
//		if (txtRecordDictionary)
//			txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary];
//		
//		dispatch_block_t bonjourBlock = ^{
//			
//			[theNetService removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
//			[theNetService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
//			[theNetService publish];
//			
//			// Do not set the txtRecordDictionary prior to publishing!!!
//			// This will cause the OS to crash!!!
//			if (txtRecordData)
//			{
//				[theNetService setTXTRecordData:txtRecordData];
//			}
//		};
//		
//		[[self class] startBonjourThreadIfNeeded];
//		[[self class] performBonjourBlock:bonjourBlock];
//	}
//}
//
//- (void)unpublishBonjour
//{
//	HTTPLogTrace();
//	
//	NSAssert(dispatch_get_specific(IsOnServerQueueKey) != NULL, @"Must be on serverQueue");
//	
//	if (netService)
//	{
//		NSNetService *theNetService = netService;
//		
//		dispatch_block_t bonjourBlock = ^{
//			
//			[theNetService stop];
//		};
//		
//		[[self class] performBonjourBlock:bonjourBlock];
//		
//		netService = nil;
//	}
//}
//
///**
// * Republishes the service via bonjour if the server is running.
// * If the service was not previously published, this method will publish it (if the server is running).
//**/
//- (void)republishBonjour
//{
//	HTTPLogTrace();
//	
//	dispatch_async(serverQueue, ^{
//		
//		[self unpublishBonjour];
//		[self publishBonjour];
//	});
//}
//
///**
// * Called when our bonjour service has been successfully published.
// * This method does nothing but output a log message telling us about the published service.
//**/
//- (void)netServiceDidPublish:(NSNetService *)ns
//{
//	// Override me to do something here...
//	// 
//	// Note: This method is invoked on our bonjour thread.
//	
//	HTTPLogInfo(@"Bonjour Service Published: domain(%@) type(%@) name(%@)", [ns domain], [ns type], [ns name]);
//}
//
///**
// * Called if our bonjour service failed to publish itself.
// * This method does nothing but output a log message telling us about the published service.
//**/
//- (void)netService:(NSNetService *)ns didNotPublish:(NSDictionary *)errorDict
//{
//	// Override me to do something here...
//	// 
//	// Note: This method in invoked on our bonjour thread.
//	
//	HTTPLogWarn(@"Failed to Publish Service: domain(%@) type(%@) name(%@) - %@",
//	                                         [ns domain], [ns type], [ns name], errorDict);
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark Notifications
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
///**
// * This method is automatically called when a notification of type HTTPConnectionDidDieNotification is posted.
// * It allows us to remove the connection from our array.
//**/
//- (void)connectionDidDie:(NSNotification *)notification
//{
//	// Note: This method is called on the connection queue that posted the notification
//	
//	[connectionsLock lock];
//	
//	HTTPLogTrace();
//	[connections removeObject:[notification object]];
//	
//	[connectionsLock unlock];
//}
//
///**
// * This method is automatically called when a notification of type WebSocketDidDieNotification is posted.
// * It allows us to remove the websocket from our array.
//**/
//- (void)webSocketDidDie:(NSNotification *)notification
//{
//	// Note: This method is called on the connection queue that posted the notification
//	
//	[webSocketsLock lock];
//	
//	HTTPLogTrace();
//	[webSockets removeObject:[notification object]];
//	
//	[webSocketsLock unlock];
//}
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#pragma mark Bonjour Thread
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
///**
// * NSNetService is runloop based, so it requires a thread with a runloop.
// * This gives us two options:
// * 
// * - Use the main thread
// * - Setup our own dedicated thread
// * 
// * Since we have various blocks of code that need to synchronously access the netservice objects,
// * using the main thread becomes troublesome and a potential for deadlock.
//**/
//
//static NSThread *bonjourThread;
//
//+ (void)startBonjourThreadIfNeeded
//{
//	HTTPLogTrace();
//	
//	static dispatch_once_t predicate;
//	dispatch_once(&predicate, ^{
//		
//		HTTPLogVerbose(@"%@: Starting bonjour thread...", THIS_FILE);
//		
//		bonjourThread = [[NSThread alloc] initWithTarget:self
//		                                        selector:@selector(bonjourThread)
//		                                          object:nil];
//		[bonjourThread start];
//	});
//}
//
//+ (void)bonjourThread
//{
//	@autoreleasepool {
//	
//		HTTPLogVerbose(@"%@: BonjourThread: Started", THIS_FILE);
//		
//		// We can't run the run loop unless it has an associated input source or a timer.
//		// So we'll just create a timer that will never fire - unless the server runs for 10,000 years.
//#pragma clang diagnostic push
//#pragma clang diagnostic ignored "-Wundeclared-selector"
//		[NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
//		                                 target:self
//		                               selector:@selector(donothingatall:)
//		                               userInfo:nil
//		                                repeats:YES];
//#pragma clang diagnostic pop
//
//		[[NSRunLoop currentRunLoop] run];
//		
//		HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE);
//	
//	}
//}
//
//+ (void)executeBonjourBlock:(dispatch_block_t)block
//{
//	HTTPLogTrace();
//	
//	NSAssert([NSThread currentThread] == bonjourThread, @"Executed on incorrect thread");
//	
//	block();
//}
//
//+ (void)performBonjourBlock:(dispatch_block_t)block
//{
//	HTTPLogTrace();
//	
//	[self performSelector:@selector(executeBonjourBlock:)
//	             onThread:bonjourThread
//	           withObject:block
//	        waitUntilDone:YES];
//}
//
//@end
